#region License
/*
The MIT License

Copyright (c) 2008 Sky Morey

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#endregion
using System.Reflection;
using System.Globalization;
using System.Collections.Generic;
using System.Abstract;
namespace System
{
    public static partial class ParserEx
    {
        #region Registration

        private static Dictionary<Type, object> _stringParserProviders = null;

        public interface IStringParserBuilder
        {
            IStringParser<TResult> Build<TResult>();
        }

        public interface IStringParser
        {
            object Parse2(string value, object defaultValue, Nparams param);
            bool Validate(string value, Nparams param);
        }

        public interface IStringParser<TResult> : IStringParser
        {
            TResult Parse(string value, TResult defaultValue, Nparams param);
            bool TryParse(string value, Nparams param, out TResult validValue);
        }

        public static void RegisterStringParser<T>(object stringParser) { RegisterStringParser(typeof(T), stringParser); }
        public static void RegisterStringParser(Type key, object stringParser)
        {
            if (_stringParserProviders == null)
                _stringParserProviders = new Dictionary<Type, object>();
            _stringParserProviders.Add(key, stringParser);
        }

        private static bool TryScanForStringParser<TResult>(out Type key, out IStringParser<TResult> stringParser)
        {
            key = null; stringParser = null; return false;
            //throw new NotImplementedException();
        }

        private static IStringParser ScanForStringParser(Type type)
        {
            if (_stringParserProviders != null)
                foreach (var stringParser2 in _stringParserProviders)
                    if ((type == stringParser2.Key) || (type.IsSubclassOf(stringParser2.Key)))
                        return (stringParser2.Value as IStringParser);
            return null;
        }
        private static IStringParser<TResult> ScanForStringParser<TResult>(Type type)
        {
            if (_stringParserProviders != null)
                foreach (var stringParser2 in _stringParserProviders)
                    if ((type == stringParser2.Key) || (type.IsSubclassOf(stringParser2.Key)))
                        return (stringParser2.Value as IStringParser<TResult>);
            Type key;
            IStringParser<TResult> stringParser;
            if (TryScanForStringParser<TResult>(out key, out stringParser))
            {
                RegisterStringParser(key, stringParser);
                return stringParser;
            }
            return null;
        }

        #endregion

        internal static class StringParserFactory<T, TResult>
        {
            private static readonly Type _type = typeof(T);
            private static readonly MethodInfo _tryParseMethodInfo = _type.GetMethod("TryParse", BindingFlags.Public | BindingFlags.Static, null, new Type[] { Internal.StringType, _type.MakeByRefType() }, null);
            public static readonly Func<string, TResult, Nparams, TResult> Parse = CreateParser(_type);
            public static readonly Func<string, object, Nparams, object> Parse2 = CreateParser2(_type);
            public static readonly ParseFormat.TryFunc<string, Nparams, TResult> TryParse = CreateTryParser(_type);
            public static readonly Func<string, Nparams, bool> Validate = CreateValidator(_type);

            static StringParserFactory() { }

            private static Func<string, TResult, Nparams, TResult> CreateParser(Type type)
            {
                if (type == Internal.BoolType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_Bool", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NBoolType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_NBool", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.DateTimeType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_DateTime", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NDateTimeType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_NDateTime", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.DecimalType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_Decimal", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NDecimalType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_NDecimal", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.Int32Type)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_Int32", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NInt32Type)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_NInt32", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.StringType)
                    return (Func<string, TResult, Nparams, TResult>)Delegate.CreateDelegate(typeof(Func<string, TResult, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("Parser_String", BindingFlags.NonPublic | BindingFlags.Static));
                var parser = ScanForStringParser<TResult>(type);
                if (parser != null)
                    return parser.Parse;
                //EnsureTryParseMethod();
                if (_tryParseMethodInfo != null)
                    return new Func<string, TResult, Nparams, TResult>(Parser_Default);
                return null;
            }
            private static Func<string, object, Nparams, object> CreateParser2(Type type)
            {
                var parser = ScanForStringParser(type);
                if (parser != null)
                    return parser.Parse2;
                //EnsureTryParseMethod();
                if (_tryParseMethodInfo != null)
                    return new Func<string, object, Nparams, object>(Parser2_Default);
                return null;
            }

            private static ParseFormat.TryFunc<string, Nparams, TResult> CreateTryParser(Type type)
            {
                if (type == Internal.BoolType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_Bool", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NBoolType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_NBool", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.DateTimeType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_DateTime", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NDateTimeType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_NDateTime", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.DecimalType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_Decimal", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NDecimalType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_NDecimal", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.Int32Type)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_Int32", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.NInt32Type)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_NInt32", BindingFlags.NonPublic | BindingFlags.Static));
                if (type == Internal.StringType)
                    return (ParseFormat.TryFunc<string, Nparams, TResult>)Delegate.CreateDelegate(typeof(ParseFormat.TryFunc<string, Nparams, TResult>), typeof(StringParserFactory<T, TResult>).GetMethod("TryParser_String", BindingFlags.NonPublic | BindingFlags.Static));
                var parser = ScanForStringParser<TResult>(type);
                if (parser != null)
                    return parser.TryParse;
                //EnsureTryParseMethod();
                if (_tryParseMethodInfo != null)
                    return new ParseFormat.TryFunc<string, Nparams, TResult>(TryParser_Default);
                return null;
            }

            private static Func<string, Nparams, bool> CreateValidator(Type type)
            {
                if (type == Internal.BoolType)
                    return new Func<string, Nparams, bool>(Validator_Bool);
                if (type == Internal.DateTimeType)
                    return new Func<string, Nparams, bool>(Validator_DateTime);
                if (type == Internal.DecimalType)
                    return new Func<string, Nparams, bool>(Validator_Decimal);
                if (type == Internal.Int32Type)
                    return new Func<string, Nparams, bool>(Validator_Int32);
                if (type == Internal.StringType)
                    return new Func<string, Nparams, bool>(Validator_String);
                var parser = ScanForStringParser(type);
                if (parser != null)
                    return parser.Validate;
                //EnsureTryParseMethod();
                if (_tryParseMethodInfo != null)
                    return new Func<string, Nparams, bool>(Validator_Default);
                return null;
            }

            private static void EnsureTryParseMethod()
            {
                if (_tryParseMethodInfo == null)
                    throw new InvalidOperationException(string.Format("{0}::TypeParse method required for parsing", typeof(T).ToString()));
            }

            #region Default

            private static TResult Parser_Default(string text, TResult defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    var args = new object[] { text, default(TResult) };
                    if ((bool)_tryParseMethodInfo.Invoke(null, BindingFlags.Default, null, args, null))
                        return (TResult)args[1];
                }
                return defaultValue;
            }
            private static object Parser2_Default(string text, object defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    var args = new object[] { text, default(TResult) };
                    if ((bool)_tryParseMethodInfo.Invoke(null, BindingFlags.Default, null, args, null))
                        return (TResult)args[1];
                }
                return defaultValue;
            }

            private static bool TryParser_Default(string text, Nparams param, out TResult validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    var args = new object[] { text, default(TResult) };
                    if ((bool)_tryParseMethodInfo.Invoke(null, BindingFlags.Default, null, args, null))
                    {
                        validValue = (TResult)args[1]; return true;
                    }
                }
                validValue = default(TResult); return false;
            }

            private static bool Validator_Default(string text, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    var args = new object[] { text, default(TResult) };
                    if ((bool)_tryParseMethodInfo.Invoke(null, BindingFlags.Default, null, args, null))
                        return true;
                }
                return false;
            }

            #endregion

            #region Bool

            private static bool Parser_Bool(string text, bool defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    switch (text.ToLowerInvariant())
                    {
                        case "1":
                        case "y":
                        case "true":
                        case "yes":
                        case "on":
                            return true;
                        case "0":
                        case "n":
                        case "false":
                        case "no":
                        case "off":
                            return false;
                    }
                    bool validValue;
                    if (bool.TryParse(text, out validValue))
                        return validValue;
                }
                return defaultValue;
            }
            private static bool? Parser_NBool(string text, bool? defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    switch (text.ToLowerInvariant())
                    {
                        case "1":
                        case "y":
                        case "true":
                        case "yes":
                        case "on":
                            return true;
                        case "0":
                        case "n":
                        case "false":
                        case "no":
                        case "off":
                            return false;
                    }
                    bool validValue;
                    if (bool.TryParse(text, out validValue))
                        return validValue;
                }
                return defaultValue;
            }

            private static bool TryParser_Bool(string text, Nparams param, out bool validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    switch (text.ToLowerInvariant())
                    {
                        case "1":
                        case "y":
                        case "true":
                        case "yes":
                        case "on":
                            validValue = true;
                            return true;
                        case "0":
                        case "n":
                        case "false":
                        case "no":
                        case "off":
                            validValue = false;
                            return true;
                    }
                    bool validValue2;
                    if (bool.TryParse(text, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = default(bool); return false;
            }
            private static bool TryParser_NBool(string text, Nparams param, out bool? validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    switch (text.ToLowerInvariant())
                    {
                        case "1":
                        case "y":
                        case "true":
                        case "yes":
                        case "on":
                            validValue = true;
                            return true;
                        case "0":
                        case "n":
                        case "false":
                        case "no":
                        case "off":
                            validValue = false;
                            return true;
                    }
                    bool validValue2;
                    if (bool.TryParse(text, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = null; return false;
            }

            private static bool Validator_Bool(string text, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    switch (text.ToLowerInvariant())
                    {
                        case "1":
                        case "y":
                        case "true":
                        case "yes":
                        case "on":
                            return true;
                        case "0":
                        case "n":
                        case "false":
                        case "no":
                        case "off":
                            return true;
                    }
                    bool validValue2;
                    return bool.TryParse(text, out validValue2);
                }
                return false;
            }

            #endregion

            #region DateTime

            private static DateTime Parser_DateTime(string text, DateTime defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    DateTime validValue;
                    if (DateTime.TryParse(text, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out validValue))
                        return validValue;
                }
                return defaultValue;
            }
            private static DateTime? Parser_NDateTime(string text, DateTime? defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    DateTime validValue;
                    if (DateTime.TryParse(text, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out validValue))
                        return validValue;
                }
                return defaultValue;
            }

            private static bool TryParser_DateTime(string text, Nparams param, out DateTime validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    DateTime validValue2;
                    if (DateTime.TryParse(text, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = default(DateTime); return false;
            }
            private static bool TryParser_NDateTime(string text, Nparams param, out DateTime? validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    DateTime validValue2;
                    if (DateTime.TryParse(text, DateTimeFormatInfo.CurrentInfo, DateTimeStyles.None, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = null; return false;
            }

            private static bool Validator_DateTime(string text, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    DateTime validValue;
                    return DateTime.TryParse(text, out validValue);
                }
                return false;
            }

            #endregion

            #region Decimal

            private static decimal Parser_Decimal(string text, decimal defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    decimal validValue;
                    if (decimal.TryParse(text, NumberStyles.Currency, null, out validValue))
                        return validValue;
                }
                return defaultValue;
            }
            private static decimal? Parser_NDecimal(string text, decimal? defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    decimal validValue;
                    if (decimal.TryParse(text, NumberStyles.Currency, null, out validValue))
                        return validValue;
                }
                return defaultValue;
            }

            private static bool TryParser_Decimal(string text, Nparams param, out decimal validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    decimal validValue2;
                    if (decimal.TryParse(text, NumberStyles.Currency, null, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = default(decimal); return false;
            }
            private static bool TryParser_NDecimal(string text, Nparams param, out decimal? validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    decimal validValue2;
                    if (decimal.TryParse(text, NumberStyles.Currency, null, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = null; return false;
            }

            private static bool Validator_Decimal(string text, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    decimal validValue;
                    return decimal.TryParse(text, NumberStyles.Currency, null, out validValue);
                }
                return false;
            }

            #endregion

            #region Int32

            private static int Parser_Int32(string text, int defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    int validValue;
                    if (int.TryParse(text, out validValue))
                        return validValue;
                }
                return defaultValue;
            }
            private static int? Parser_NInt32(string text, int? defaultValue, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    int validValue;
                    if (int.TryParse(text, out validValue))
                        return validValue;
                }
                return defaultValue;
            }

            private static bool TryParser_Int32(string text, Nparams param, out int validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    int validValue2;
                    if (int.TryParse(text, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = default(int); return false;
            }
            private static bool TryParser_NInt32(string text, Nparams param, out int? validValue)
            {
                if ((text != null) && (text.Length > 0))
                {
                    int validValue2;
                    if (int.TryParse(text, out validValue2))
                    {
                        validValue = validValue2; return true;
                    }
                }
                validValue = null; return false;
            }

            private static bool Validator_Int32(string text, Nparams param)
            {
                if ((text != null) && (text.Length > 0))
                {
                    int validValue;
                    return int.TryParse(text, out validValue);
                }
                return false;
            }

            #endregion

            #region String

            private static string Parser_String(string text, string defaultValue, Nparams param)
            {
                string value2;
                if ((text != null) && ((value2 = text.Trim()).Length > 0))
                    return value2;
                return defaultValue;
            }

            private static bool TryParser_String(string text, Nparams param, out string validValue)
            {
                string value2;
                if ((text != null) && ((value2 = text.Trim()).Length > 0))
                {
                    validValue = value2; return true;
                }
                validValue = default(string); return false;
            }

            private static bool Validator_String(string text, Nparams param)
            {
                return ((text != null) && (text.Trim().Length > 0));
            }

            #endregion
        }
    }
}
